/* A simple server in the internet domain using TCP
   The port number is passed as an argument 
   This version runs forever, forking off a separate 
   process for each connection
*/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <iostream>
#include <string.h>
#include <map>
#include "Frame.h"
#include <locale>
#include <signal.h>
#include <sched.h>
#include <sys/shm.h>
 

 typedef struct client_instance
{
        client_instance(){}
        client_instance(const client_instance &ci)
        {
                id=ci.id;
                pid=ci.pid;
                socket=ci.socket;
                tiempo=ci.tiempo;
                strcpy(nombre,ci.nombre);
        }
        long id;
        pid_t pid;
        int socket;
        int tiempo;
        char nombre[64]; 
};

typedef struct result
{
    bool found;
    int pos;
};


typedef struct params
{
    char nombre[64];
    int socket;
    int pos;
    int m_id;
    bool reconexion;
};

static int pos=0;

result finder (long hash, client_instance *puntero)
{
    result r;
    int j,pus;
    pus=10;
    std::cout<<"el pus en finder: "<<pus<<std::endl; 
    std::cout<<"el hash en finder: "<<int(hash)<<std::endl;
    for(j=1;j<=pus;j++)
    {
        std::cout<<"Nombre en el registro: "<<puntero[j].nombre<<std::endl;
        std::cout<<"Tiempo en el registro: "<<puntero[j].tiempo<<std::endl;
        if(puntero[j].id==hash)
        {
            r.found=true;
            r.pos=j;
            return (r);
            break;
        }
        else
        {
            r.found=false;
            r.pos=0;
        }
    }
    
    return (r);
}

int share_memory()
{
        key_t Clave;
	int Id_Memoria;

	Clave = ftok ("/bin/ls",50);
	if (Clave == -1)
	{
		std::cout << "No consigo clave para memoria compartida" << std::endl;
		exit(0);
	}
        
        Id_Memoria = shmget (Clave, sizeof(client_instance)*100, 0777 | IPC_CREAT);
	if (Id_Memoria == -1)
	{
		std::cout << "No consigo Id para memoria compartida" << std::endl;
		exit(0);
	}
        return (Id_Memoria);
        
}


int Message_queue_setup(const int & queue_key)
{
    int id_message_queue;
    key_t key; 
    key = ftok ("/bin/ls", queue_key);
    if (key == (key_t)-1)
    {
        std::cout << "Error al obtener clave para cola mensajes" << std::endl;
        return(EXIT_FAILURE);
    } 
    
        id_message_queue = msgget (key, 0600 | IPC_CREAT);
    if (id_message_queue == -1)
    {
        std::cout << "Error al obtener identificador para cola mensajes" << std::endl;
        return(EXIT_FAILURE);
    }
        
    return (id_message_queue);
}

int cola_1(void*)
{
    std::cout<<"estoy corriendo\n";
    int key,key_2,id_memoria,pus;
    message_queue bookmark;
    message_queue outbound_mail;
    id_memoria=share_memory();
    key=Message_queue_setup(I_QUEUE_KEY);
    key_2=Message_queue_setup(C_PACKER_QUEUE_KEY);
    std::cout<<"Id_memoria en la cola: " <<id_memoria<<std::endl;
    std::locale loc;
    const std::collate<char>& coll = std::use_facet<std::collate<char> >(loc);
    std::string seed;
    long hash;
    client_instance *puntero;
    char port[6];
    result r;
    puntero=(client_instance *)shmat (id_memoria, (char *)0, 0);
    if (puntero == NULL)
	{
		std::cout << "No consigo memoria compartida" << std::endl;
		exit (0);
	}
     
    
    while(true)
    {
        sleep(1);
        msgrcv(key, (struct msgbuf *)&outbound_mail,
        sizeof(outbound_mail.package)+sizeof(outbound_mail.cli_addr),0,0);
        
        print_output(outbound_mail.package);
        seed=inet_ntoa(outbound_mail.package.id_client.sin_addr);
        
        if(outbound_mail.package.header.type_message==ADOPTION_PAPERS)
        {
            
            sprintf(port,"%d",ntohs(outbound_mail.package.id_client.sin_port)); 
            seed+=port;
            std::cout<<"esta es la semilla en A.P.: "<<seed<<std::endl;
            hash=coll.hash(seed.data(),seed.data()+seed.length());
            std::cout<<"el hash en A.P.: "<<int(hash)<<std::endl;
            r=finder(hash, puntero);
            std::cout<<"Es un mensaje de adopcion\n";
            std::cout<<"Este es el pid: "<<puntero[r.pos].pid<<std::endl;
            std::cout<<"Este es el pos: "<<r.pos<<std::endl;
            if(puntero[r.pos].pid)
                kill(puntero[r.pos].pid, SIGTERM);
            
            close(puntero[r.pos].socket);
            
            bzero((char *) &bookmark, sizeof(bookmark));   
            bookmark.package=outbound_mail.package;
            bookmark.cli_addr=outbound_mail.package.ip_server_guardian;
            bookmark.package.bookmark.tiempo=puntero[r.pos].tiempo;
            strcpy(bookmark.package.bookmark.nombre,puntero[r.pos].nombre);
            bookmark.package.header.id_server_destination=outbound_mail.package.id_server_guardian;
            bookmark.package.header.id_server_source=outbound_mail.package.header.id_server_destination; 
            bookmark.package.header.offset+=sizeof(bookmark.package.bookmark);          
            bookmark.package.header.type_message=BOOKMARK;
            bookmark.id_message=HIGHT_PRIORITY;
            bookmark.package.id_client=outbound_mail.package.id_client;
            msgsnd(key_2, (struct msgbuf *)&bookmark,
            sizeof(bookmark.package)+sizeof(bookmark.cli_addr),IPC_NOWAIT);
            puntero[r.pos].id=0; 
          
       
        }
        
        else if(outbound_mail.package.header.type_message==BOOKMARK)
        {           
            std::cout<<"he procesado el bookmar\n";
            pos++;
            pus=pos;
            sprintf(port,"%d",ntohs(outbound_mail.package.id_client.sin_port)+1); 
            seed+=port;
            std::cout<<"esta es la semilla en Bookmark: "<<seed<<std::endl;
            hash=coll.hash(seed.data(),seed.data()+seed.length());
            std::cout<<"el hash en Bookmark: "<<int(hash)<<std::endl;
            
            puntero[pus].id=hash;
            strcpy(puntero[pus].nombre,outbound_mail.package.bookmark.nombre);
            puntero[pus].tiempo=outbound_mail.package.bookmark.tiempo;
            std::cout<<"Tiempo en el bookmark: "<<puntero[pus].tiempo<<std::endl;
            char msg[]="ready";
            int sockfd, n;
            struct sockaddr_in serv_addr;
            unsigned int serv_leng=sizeof(serv_addr);
            bzero((char *) &serv_addr, sizeof(serv_addr));
            sockfd = socket(AF_INET, SOCK_DGRAM, 0);
            serv_addr.sin_family = AF_INET;
            serv_addr.sin_addr=outbound_mail.package.id_client.sin_addr;
            serv_addr.sin_port = htons(666);
      
            n = sendto(sockfd, &msg,sizeof(msg), 0, (struct sockaddr *)&serv_addr, serv_leng);
            std::cout<<"Bytes enviados: "<<n<<std::endl;
            close(sockfd);
         
  
        }
        
   }
    return(0);
 } 
int dostuff(void *);
void error(const char *msg)
{
    std::cout<<"error"<<std::endl;
    perror(msg);
    exit(1);
}

int main(int argc, char *argv[])
{
     
     params args;
     void *stack=malloc(65536);
     client_instance *puntero;
     int sockfd, newsockfd, portno, optval;
     pid_t pid;
     socklen_t clilen;
     struct sockaddr_in serv_addr, cli_addr;
     char port[6];
     socklen_t optlen = sizeof(optval);
     std::string seed;
     long hash;
     std::locale loc;
     const std::collate<char>& coll = std::use_facet<std::collate<char> >(loc);
     int Id_Memoria, pus;
     result r;
     int n;
     
     if (argc < 2) {
         fprintf(stderr,"ERROR, no port provided\n");
         exit(1);
     }
     
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     
     optval = 1;
     optlen = sizeof(optval);
     if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) 
         perror("setsockopt()");
     
    
     if(sockfd < 0) 
        error("ERROR opening socket");
    
     bzero((char *) &serv_addr, sizeof(serv_addr));
     portno = atoi(argv[1]);
     serv_addr.sin_family = AF_INET;
     serv_addr.sin_addr.s_addr = INADDR_ANY;
     serv_addr.sin_port = htons(portno);
   
     if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) 
         error("ERROR on binding");
     
     listen(sockfd,5);
     clilen = sizeof(cli_addr);
     
     Id_Memoria=share_memory();
     puntero = (client_instance *)shmat (Id_Memoria, (char *)0, 0);
	if (puntero == NULL)
	{
		std::cout << "No consigo memoria compartida" << std::endl;
		exit (0);
	}
     
     pid=clone(cola_1,stack+65536,0,NULL);
     args.m_id=Id_Memoria;
     std::cout<<"Id_memoria en la creacion: " <<Id_Memoria<<std::endl;
     if (pid > 0)
     {
       
         while (true) 
         {
             newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr, &clilen);
         
             if (newsockfd < 0)     
                 error("ERROR on accept");
         
             seed=inet_ntoa(cli_addr.sin_addr);
             sprintf(port,"%d",ntohs(cli_addr.sin_port)); 
             seed+=port;
             std::cout<<"esta es la semilla: "<<seed<<std::endl;
             hash=coll.hash(seed.data(),seed.data()+seed.length());
             std::cout<<"Este es el hash: "<<int(hash)<<std::endl;
             
             
             args.socket=newsockfd;
             r=finder(hash,puntero);  
             if (r.found==false)
             {
                 pos++; 
                 pus=pos;
                 args.reconexion=false;
                 bzero((char *) &args.nombre, sizeof(args.nombre));
                 n = read(newsockfd,args.nombre,64);
                 if(n < 0)
                        error("ERROR reading the socket");
                 
                 std::cout<<"bien con el read: "<<args.nombre<<std::endl;
                 args.pos=pus;
                 puntero[pus].id=hash;
                 strcpy(puntero[pus].nombre,args.nombre);
                 puntero[pus].socket=newsockfd;
                 puntero[pus].tiempo=0;
                 void* arg =(void*)&args;
                 puntero[pus].pid=clone(dostuff, stack+65536,0, arg);  
             }
             
             else
             {
                 args.reconexion=true;
                 args.pos=r.pos;
                 puntero[r.pos].socket=newsockfd;
                 std::cout<<"En la reconexión el tiempo es: "<<puntero[r.pos].tiempo<<std::endl;
                 std::cout<<"En la reconexión el nombre es: "<<puntero[r.pos].nombre<<std::endl;
                 void* arg =(void*)&args;
                 puntero[r.pos].pid=clone(dostuff, stack+65536,0, arg);
                 
             }
             
         }  /* end of while */
     }
     close(sockfd);
     return 0; /* we never get here */
}

/******** DOSTUFF() *********************
 There is a separate instance of this function 
 for each connection.  It handles all communication
 once a connnection has been established.
 *****************************************/
int dostuff (void *args)
{
    params *arg;
    int r,n,pos,socket;
    char buffer[255];
    char cont[10];
    char nombre[64];
    char id[]="Soy el servidor A y has estado conmigo: ";
    client_instance *puntero;
    
    arg=(params*)malloc(sizeof(params));  
    arg=(params*)args;
    puntero = (client_instance *)shmat (arg->m_id, (char *)0, 0);
    pos=arg->pos;
    socket=arg->socket;
    if (arg->reconexion)
    {
        r=puntero[pos].tiempo;
        strcpy(nombre,puntero[pos].nombre);
    }
    else
    {
        r=0;
        strcpy(nombre,arg->nombre);
        
    }
    
    while(true)
    {
       sleep (1);
       bzero((char *) &buffer, sizeof(buffer));
       bzero((char *) &cont, sizeof(cont));
       r++;
       puntero[pos].tiempo=r;
       std::cout<<"El pos:"<<pos<<" en el Hilo de:"<<puntero[pos].nombre<<"y mi pid es:"<<puntero[pos].pid<<std::endl;
       sprintf(cont,"%d",r);
       strcat(buffer,"\nHola: ");
       strcat(buffer,nombre);
       strcat(buffer,id);
       strcat(buffer,cont);
       strcat(buffer," segundos\n");
       n = write(socket,buffer,sizeof(buffer));
       if (n < 0) 
           std::cout<<"error en write"<<std::endl;
       
   }
}
